/**********************************************************************\

 Copyright 2012 Diggory Blake

 This file is part of klip-engine

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

\**********************************************************************/

#include "keffectcg.h"
#include "kencoding.h"
#include "klog.h"
#include "kengine.h"

bool isCGSampler(CGtype type) {
	switch (type) {
	case CG_SAMPLER:
	case CG_SAMPLER1D:
	case CG_SAMPLER1DSHADOW:
	case CG_SAMPLER2D:
	case CG_SAMPLER2DSHADOW:
	case CG_SAMPLER3D:
	case CG_SAMPLERCUBE:
	case CG_SAMPLERRECT:
	case CG_SAMPLERRECTSHADOW:
		return true;
	default:
		return false;
	}
}
void kEffectCg::init(CGcontext context, const char* src) {
	_effect = cgCreateEffect(context, src, 0);

	if (!_effect) {
		std::string errorMsg = cgGetLastListing(context);
		std::tstring errorDest;
		kEncodingConverter<kEncoding<char>, kEncoding<>>::convert(errorMsg, errorDest);

		K_LOGF(_engine, keLog_Error, _T("Error creating Cg effect: %s"), errorDest.c_str());
		throw kError(_T("Cg compiler error"));
	}

	_technique = cgGetFirstTechnique(_effect);
	while (_technique && !cgValidateTechnique(_technique)) {
		_technique = cgGetNextTechnique(_technique);
	}

	if (!_technique) {
		std::string errorMsg = cgGetLastListing(context);
		std::tstring errorDest;
		kEncodingConverter<kEncoding<char>, kEncoding<>>::convert(errorMsg, errorDest);

		K_LOGF(_engine, keLog_Error, _T("Error creating Cg effect: %s"), errorDest.c_str());
		throw kError(_T("Cg compiler error"));
	}

	CGparameter param = cgGetFirstEffectParameter(_effect);
	int textureIndex = 0;
	while (param && (textureIndex < 8)) {
		CGtype paramType = cgGetParameterType(param);
		if (isCGSampler(paramType)) {
			_textureParams[textureIndex++] = param;
		} else if (paramType == CG_ARRAY) {
			paramType = cgGetArrayType(param);
			if (isCGSampler(paramType)) {
				int n = cgGetArrayTotalSize(param);
				for (int i = 0; (i < n) && (textureIndex < 8); i++) {
					CGparameter param2 = cgGetArrayParameter(param, i);
					_textureParams[textureIndex++] = param2;
				}
			}
		}
		param = cgGetNextParameter(param);
	}
	for (; textureIndex < 8; textureIndex++) {
		_textureParams[textureIndex] = 0;
	}
}
kEffectCg::kEffectCg(kEngine* engine, CGcontext context, const kDataView& source) {
	_engine = engine;
	kDataBuffer code(source);
	code.resizeFill(code.getSize() + 1, 0);
	init(context, (const char*)code.getPtr());
}
kEffectCg::kEffectCg(kEngine* engine, CGcontext context, kStream* source) {
	_engine = engine;
	kDataBuffer code(source);
	code.resizeFill(code.getSize() + 1, 0);
	init(context, (const char*)code.getPtr());
}
kEffectCg::~kEffectCg() {
	cgDestroyEffect(_effect);
}
kHandle kEffectCg::getParamHandle(const std::tstring& name) {
	std::string dest;
	kEncodingConverter<kEncoding<>, kEncoding<char>>::convert(name, dest);
	return cgGetNamedEffectParameter(_effect, dest.c_str());
}
void kEffectCg::setParam(kHandle handle, size_t n, const int* v) {
	cgSetParameterValueic((CGparameter)handle, n, v);
}
void kEffectCg::setParam(kHandle handle, size_t n, const float* v) {
	cgSetParameterValuefc((CGparameter)handle, n, v);
}
void kEffectCg::unsetParam(kHandle handle) {
	CGtype type = cgGetParameterBaseType((CGparameter)handle);
	int n = cgGetParameterRows((CGparameter)handle)*cgGetParameterColumns((CGparameter)handle);
	switch (type) {
	case CG_INT:
		{
			int v[16];
			cgGetParameterDefaultValueic((CGparameter)handle, n, v);
			cgSetParameterValueic((CGparameter)handle, n, v);
		}
		break;
	case CG_FLOAT:
		{
			float v[16];
			cgGetParameterDefaultValuefc((CGparameter)handle, n, v);
			cgSetParameterValuefc((CGparameter)handle, n, v);
		}
		break;
	case CG_DOUBLE:
		{
			double v[16];
			cgGetParameterDefaultValuedc((CGparameter)handle, n, v);
			cgSetParameterValuedc((CGparameter)handle, n, v);
		}
		break;
	}
}
